package com.bsj.sipgateway.web.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsj.power.common.config.exception.ApiException;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.def.dto.FaultDTO;
import com.bsj.power.common.def.dto.FaultLogDTO;
import com.bsj.power.common.def.dto.RedisDeviceInfo;
import com.bsj.power.common.def.dto.alarmReport.SceneFaultDTO;
import com.bsj.power.common.def.dto.video.FaultRecordDTO;
import com.bsj.power.common.def.entity.Fault;
import com.bsj.power.common.def.entity.FaultLog;
import com.bsj.power.common.def.entity.alarmReport.AlarmReport;
import com.bsj.power.common.def.entity.job.Job;
import com.bsj.power.common.def.vo.DeviceDurationVO;
import com.bsj.power.common.def.vo.FaultVO;
import com.bsj.power.common.def.vo.SceneFaultVO;
import com.bsj.power.common.def.vo.video.JobNumListVO;
import com.bsj.power.common.mapper.DeviceDurationMapper;
import com.bsj.power.common.mapper.FaultLogMapper;
import com.bsj.power.common.mapper.FaultMapper;
import com.bsj.power.common.mapper.JobMapper;
import com.bsj.power.common.util.DateUtils;
import com.bsj.power.common.util.StringUtils;
import com.bsj.sipgateway.web.service.IDeviceService;
import com.bsj.sipgateway.web.service.IFaultService;
import io.lettuce.core.resource.DefaultEventLoopGroupProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import redis.clients.bsj.JedisClusterBSJ;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author zhaoyang
 * @Date 2024/7/3 15:11
 */
@Slf4j
@Service("faultServiceImpl")
public class FaultServiceImpl implements IFaultService {

    @Autowired
    private FaultMapper faultMapper;

    @Autowired
    private FaultLogMapper faultLogMapper;

    @Autowired
    private DeviceDurationMapper deviceDurationMapper;

    @Resource
    private JobMapper jobMapper;

    @Autowired
    private JedisClusterBSJ jedisClusterBSJ;

    @Autowired
    @Qualifier("deviceServiceImpl")
    private IDeviceService deviceService;

    @Override
    public boolean add(FaultDTO faultDTO) {
        Fault fault = new Fault();
        BeanUtils.copyProperties(faultDTO, fault);
        faultMapper.insert(fault);

        // 插入设备故障维护日志
        FaultLog faultLog = new FaultLog();
        faultLog.setNickName(fault.getNickName());
        faultLog.setDeviceId(fault.getDeviceNumber());
//        faultLog.setJobId(fault.getJobId());
//        Job job = jobMapper.selectById(faultDTO.getJobId());
//        if (job != null && job.getJobNum() != null) {
//            faultLog.setJobNum(job.getJobNum());
//        }
        faultLog.setFaultTime(fault.getFaultDate());
        faultLog.setType(1);
        faultLogMapper.insert(faultLog);
        return true;
    }

    @Override
    public boolean deleteById(Integer id) {
        Fault fault = faultMapper.selectById(id);

        // 插入设备故障维护日志
        FaultLog faultLog = new FaultLog();
        faultLog.setNickName(fault.getNickName());
        faultLog.setDeviceId(fault.getDeviceNumber());
//        faultLog.setJobId(fault.getJobId());
//        Job job = jobMapper.selectById(fault.getJobId());
//        if (job != null && job.getJobNum() != null) {
//            faultLog.setJobNum(job.getJobNum());
//        }
        faultLog.setFaultTime(fault.getFaultDate());
        faultLog.setType(2);
        faultLogMapper.insert(faultLog);

        return faultMapper.deleteById(id) > 0;
    }

    @Override
    public boolean update(FaultDTO faultDTO) {
        Fault fault = new Fault();
        BeanUtils.copyProperties(faultDTO, fault);
        Integer handle = fault.getHandle();
        // 设置为故障已处理的时候把缓存故障标志清空
        if (handle != null && handle == 1) {
            String s = jedisClusterBSJ.get(RedisConstant.REDIS_DEVICE_INFO + fault.getDeviceNumber());
            if (s != null) {
                RedisDeviceInfo redisDeviceInfo = JSON.parseObject(s, RedisDeviceInfo.class);
                redisDeviceInfo.setFault(0);
                jedisClusterBSJ.set(RedisConstant.REDIS_DEVICE_INFO + fault.getDeviceNumber(), JSON.toJSONString(redisDeviceInfo));
            }
        }

        faultMapper.updateById(fault);

        // 插入设备故障维护日志
        if (faultDTO.getRepairman() != null) {
            FaultLog faultLog = new FaultLog();
            faultLog.setNickName(fault.getNickName());
            faultLog.setDeviceId(fault.getDeviceNumber());
            faultLog.setFaultTime(fault.getFaultDate());
            faultLog.setType(4);
            faultLogMapper.insert(faultLog);

            // 指派以后，设置一个完结的日志，等到设备注册成功以后，写入到日志中
            if (faultLog.getType() == 4) {
                faultLog.setId(null); // 会自动引用最新主键，导致主键重复，每次重新写
                faultLog.setType(5);
                jedisClusterBSJ.set(RedisConstant.FAULT_LOG_FINISH + faultLog.getDeviceId(), JSON.toJSONString(faultLog));
            }
        }

        return true;
    }

    @Override
    public JsonResult findFaultList(FaultRecordDTO faultRecordDTO) {
        List<FaultVO> vos = new ArrayList<>();

        String vagueStr = faultRecordDTO.getVagueStr();
        String beginTime = faultRecordDTO.getBeginTime();
        String endTime = faultRecordDTO.getEndTime();
        Integer pageNumber = faultRecordDTO.getPageNumber();
        Integer pageSize = faultRecordDTO.getPageSize();
        int count = faultMapper.selectPageAndConditionCount(vagueStr, beginTime, endTime);
        if (count == 0) {
            return JsonResult.success(vos, 0);
        }
        Integer beginIndex = (pageNumber - 1) * pageSize;
        List<Fault> faultList = faultMapper.selectPageAndCondition(vagueStr, beginTime, endTime, beginIndex, pageSize);

        if (CollectionUtil.isNotEmpty(faultList)) {
            Map<Long, Job> jobMap = new HashMap<>();
            List<Long> jobIds = faultList.stream().filter(v -> v.getJobId() != null).mapToLong(Fault::getJobId).boxed().collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(jobIds)) {
                List<Job> jobList = jobMapper.selectList(new QueryWrapper<Job>().select("job_id", "job_num").in("job_id", jobIds));
                if (CollectionUtil.isNotEmpty(jobList)) {
                    jobMap = jobList.stream().collect(Collectors.toMap(Job::getJobId, Function.identity()));
                }
            }
            Map<Long, Job> finalJobMap = jobMap;
            faultList.forEach(fault -> {
                FaultVO vo = new FaultVO();
                BeanUtils.copyProperties(fault, vo);
                vo.setFaultDate(DateUtils.dateToStr(fault.getFaultDate()));
                vo.setCreateTime(DateUtils.dateToStr(fault.getCreateTime()));
                vo.setUpdateTime(DateUtils.dateToStr(fault.getUpdateTime()));
                if (fault.getJobId() != null) {
                    Job job = finalJobMap.get(fault.getJobId());
                    if (job != null) {
                        vo.setJobId(job.getJobId());
                        vo.setJobNum(job.getJobNum());
                    }
                }
                vos.add(vo);
            });
        }
        // 事故日期倒序
        List<FaultVO> createTimeSortedVos = vos.stream().sorted(Comparator.comparing(FaultVO::getFaultDate).reversed()).collect(Collectors.toList());

        return JsonResult.success(createTimeSortedVos, count);

    }

    /**
     * @param faultRecordDTO
     * @return
     */
    @Override
    public List<FaultVO> faultListExport(FaultRecordDTO faultRecordDTO) {
        List<FaultVO> vos = new ArrayList<>();

        String vagueStr = faultRecordDTO.getVagueStr();
        String beginTime = faultRecordDTO.getBeginTime();
        String endTime = faultRecordDTO.getEndTime();
        Integer pageNumber = faultRecordDTO.getPageNumber();
        Integer pageSize = faultRecordDTO.getPageSize();
//        int count = faultMapper.selectPageAndConditionCount(vagueStr, beginTime, endTime);
//        if (count == 0) {
//            return JsonResult.success(vos, 0);
//        }
//        Integer beginIndex = (pageNumber - 1) * pageSize;
        if (StringUtils.isEmpty(vagueStr) || "\"\"".equals(vagueStr) || "\'\'".equals(vagueStr)) {
            vagueStr = null;
        }
        List<Fault> faultList = faultMapper.selectPageAndCondition(vagueStr, beginTime, endTime, null, null);

        if (CollectionUtil.isNotEmpty(faultList)) {
            Map<Long, Job> jobMap = new HashMap<>();
            List<Long> jobIds = faultList.stream().filter(v -> v.getJobId() != null).mapToLong(Fault::getJobId).boxed().collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(jobIds)) {
                List<Job> jobList = jobMapper.selectList(new QueryWrapper<Job>().select("job_id", "job_num").in("job_id", jobIds));
                if (CollectionUtil.isNotEmpty(jobList)) {
                    jobMap = jobList.stream().collect(Collectors.toMap(Job::getJobId, Function.identity()));
                }
            }
            Map<Long, Job> finalJobMap = jobMap;
            faultList.forEach(fault -> {
                FaultVO vo = new FaultVO();
                BeanUtils.copyProperties(fault, vo);
                vo.setFaultDate(DateUtils.dateToStr(fault.getFaultDate()));
                vo.setCreateTime(DateUtils.dateToStr(fault.getCreateTime()));
                vo.setUpdateTime(DateUtils.dateToStr(fault.getUpdateTime()));
                if (fault.getJobId() != null) {
                    Job job = finalJobMap.get(fault.getJobId());
                    if (job != null) {
                        vo.setJobId(job.getJobId());
                        vo.setJobNum(job.getJobNum());
                    }
                }
                vos.add(vo);
            });
        }
        // 事故日期倒序
        List<FaultVO> createTimeSortedVos = vos.stream().sorted(Comparator.comparing(FaultVO::getFaultDate).reversed()).collect(Collectors.toList());

        return createTimeSortedVos;


    }

    @Override
    public List<DeviceDurationVO> selectDeviceDurationByTime(String beginTime, String endTime) {
        return deviceDurationMapper.selectDeviceDurationByTime(beginTime, endTime);
    }

    @Override
    public List<Fault> selectByTypeAndTime(String beginTime, String endTime, Integer type) {
        return faultMapper.selectByTypeAndTime(beginTime, endTime, type);
    }

    @Override
    public JsonResult jobNumList(String jobNum) {
        List<JobNumListVO> jobNumListVOList = new LinkedList<>();
        QueryWrapper<Job> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("job_id", "job_num");
        if (StringUtils.isNotEmpty(jobNum)) {
            queryWrapper.like("job_num", jobNum.trim());
        }
        queryWrapper.orderByDesc("job_id");
        queryWrapper.last("limit 10");
        List<Job> jobList = jobMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(jobList)) {
            jobList.forEach(v -> {
                JobNumListVO jobNumListVO = new JobNumListVO();
                BeanUtils.copyProperties(v, jobNumListVO);
                jobNumListVOList.add(jobNumListVO);
            });
        }
        return JsonResult.success(jobNumListVOList);
    }

    @Override
    public JsonResult sceneDeviceFaultStatistics(String beginTime, String endTime) {
        if (beginTime == null || endTime == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        QueryWrapper<Fault> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("create_time", beginTime, endTime);
        List<Fault> faultList = faultMapper.selectList(queryWrapper);
        JsonResult jsonResult = new JsonResult();
        if (CollectionUtils.isEmpty(faultList)) {
            jsonResult.setCode(-1);
            jsonResult.setMsg("暂无数据");
            return jsonResult;
        }
        List<String> deviceIds = faultList.stream().map(Fault::getDeviceNumber).distinct().collect(Collectors.toList());
        List<SceneFaultDTO> sceneFaultDTOS = deviceService.selectSceneFaultDTOByDeviceIds(deviceIds);
        if (CollectionUtils.isEmpty(sceneFaultDTOS)) { // 所有故障设备都没有绑定场景
            jsonResult.setCode(-1);
            jsonResult.setMsg("暂无数据");
            return jsonResult;
        }
        // 先按照场景分组
        List<SceneFaultVO> vos = new ArrayList<>();
        Map<Integer, List<SceneFaultDTO>> collect = sceneFaultDTOS.stream().collect(Collectors.groupingBy(SceneFaultDTO::getSceneId));
        for (Map.Entry<Integer, List<SceneFaultDTO>> entry : collect.entrySet()) {
            Integer sceneId = entry.getKey();
            List<SceneFaultDTO> value = entry.getValue();
            List<String> sceneDeviceIds = value.stream().map(SceneFaultDTO::getDeviceId).collect(Collectors.toList());
            SceneFaultVO sceneFaultVO = new SceneFaultVO();
            sceneFaultVO.setSceneId(sceneId);
            sceneFaultVO.setSceneName(value.get(0).getSceneName());
            int i = 0;
            for (Fault fault : faultList) {
                String deviceNumber = fault.getDeviceNumber();
                if (sceneDeviceIds.contains(deviceNumber)) {
                    i++;
                }
            }
            sceneFaultVO.setAlarmCount(i);
            vos.add(sceneFaultVO);
        }
        vos = vos.stream().sorted(Comparator.comparing(SceneFaultVO::getAlarmCount).reversed()).collect(Collectors.toList());
        // 如果大于10，就取前10个
        if (vos.size() > 10) {
            vos = vos.stream().limit(10).collect(Collectors.toList());
        }
        jsonResult.setCode(JsonResultEnum.SUCCESS.getCode());
        jsonResult.setMsg(JsonResultEnum.SUCCESS.getMsg());
        jsonResult.setData(vos);
        jsonResult.setTotal(vos.size());
        return jsonResult;
    }

    @Override
    public IPage<FaultLog> pageFaultLogList(FaultLogDTO faultLogDTO) {
        QueryWrapper<FaultLog> queryWrapper = new QueryWrapper<>();
        if (faultLogDTO.getType() != null) {
            queryWrapper.eq("type", faultLogDTO.getType());
        }
        if (faultLogDTO.getNickName() != null) {
            queryWrapper.like("nick_name", "%" + faultLogDTO.getNickName() + "%");
        }
        if (faultLogDTO.getBeginTime() != null && faultLogDTO.getEndTime() != null) {
            queryWrapper.between("create_time", faultLogDTO.getBeginTime(), faultLogDTO.getEndTime());
        }
        queryWrapper.orderByDesc("create_time");
        IPage<FaultLog> faultLogPage = new Page<>(faultLogDTO.getPageNumber(), faultLogDTO.getPageSize());
        IPage<FaultLog> faultLogIPage = faultLogMapper.selectPage(faultLogPage, queryWrapper);
        return faultLogIPage;
    }
}
